/*
 * BinaryGeneticAlgorithm.cpp
 *
 *  Created on: Oct 17, 2013
 *      Author: filipe
 */
#include <time.h>
#include <cstdlib>
#include <float.h>
#include <limits.h>
#include "BinaryGeneticAlgorithm.h"


BinaryGeneticAlgorithm::BinaryGeneticAlgorithm(BinaryGeneticAlgorithmParameters *parameters, double (*fitness_function) (double *data))
{
	srand(time(NULL));

	if (parameters == NULL)
		_parameters = new BinaryGeneticAlgorithmParameters();
	else
		_parameters = parameters;

	_fitness_function = fitness_function;
	_dimension = _parameters->num_parameters_to_optimize;

	_data = (unsigned int*) calloc (parameters->num_individuals * _dimension, sizeof(unsigned int));
	_current_estimate = (double *) calloc (_parameters->num_parameters_to_optimize, sizeof(double));
	_individual_estimate = (double *) calloc (_parameters->num_parameters_to_optimize, sizeof(double));
	_fitness_per_individual = (double *) calloc (_parameters->num_individuals, sizeof(double));

	// initialize the data with random values
	for (int i = 0; i < _parameters->num_individuals * _dimension; i++)
		_data[i] = rand();

	_UpdateCurrentEstimate();
}


BinaryGeneticAlgorithm::~BinaryGeneticAlgorithm()
{
	delete(_data);
	delete(_parameters);
	delete(_current_estimate);
}


int
BinaryGeneticAlgorithm::_GetIndividualWithMaximumFitness(void) const
{
	int id = 0;
	double max_fitness = -DBL_MAX;

	for (int i = 0; i < _parameters->num_individuals; i++)
	{
		if (_fitness_per_individual[i] > max_fitness)
		{
			max_fitness = _fitness_per_individual[i];
			id = i;
		}
	}

	return id;
}


double *
BinaryGeneticAlgorithm::_CalculateEstimateOfIndividual(int individual) const
{
	double parameter_variation; // TODO: compute it only once in the constructor and store the result

	for (int i = 0; i < _parameters->num_parameters_to_optimize; i++)
	{
		parameter_variation = (_parameters->max_permited_value[i] - _parameters->min_permited_value[i]);
		_individual_estimate[i] = ((double)_data[individual * _parameters->num_parameters_to_optimize + i] / (double) INT_MAX) * parameter_variation + _parameters->min_permited_value[i];
	}

	return _individual_estimate;
}


void
BinaryGeneticAlgorithm::_UpdateCurrentEstimate(void)
{
	int best_individual;
	double parameter_variation;

	best_individual = _GetIndividualWithMaximumFitness();

	for (int i = 0; i < _parameters->num_parameters_to_optimize; i++)
	{
		parameter_variation = (_parameters->max_permited_value[i] - _parameters->min_permited_value[i]);
		_current_estimate[i] = ((double)_data[best_individual * _parameters->num_parameters_to_optimize + i] / (double) INT_MAX) * parameter_variation + _parameters->min_permited_value[i];
	}
}


void
BinaryGeneticAlgorithm::_UpdateFitness()
{
	for (int i = 0; i < _parameters->num_individuals; i++)
		_fitness_per_individual[i] = (*_fitness_function)(_CalculateEstimateOfIndividual(i));
}


void
BinaryGeneticAlgorithm::_GenerateNewIndividuals()
{
	// TODO:
}


void
BinaryGeneticAlgorithm::Optimize()
{
	for (int i = 0; i < _parameters->num_max_iteractions; i++)
	{
		_UpdateFitness();
		_GenerateNewIndividuals();

		// TODO: check other stop conditions
	}
}


double*
BinaryGeneticAlgorithm::GetCurrentEstimate()
{
	_UpdateCurrentEstimate();
	return _current_estimate;
}

